import asyncio, time
from PySide6.QtCore import QObject, Signal
from bleak import BleakClient
from threading import Thread

from queue import Queue
from bleak.backends.characteristic import BleakGATTCharacteristic

from Communicate.BleFunction.BleDeviceInfo import BleDeviceInfo

TX_CHARACTERISTIC = "0000abf1-0000-1000-8000-00805f9b34fb"
RX_CHARACTERISTIC = "0000abf2-0000-1000-8000-00805f9b34fb"

class BleClient(QObject):
    Connected = Signal(BleDeviceInfo)
    ConnectFailed = Signal()
    Disconnected = Signal()
    DataReceived = Signal(str)

    def __init__(self, bleDeviceInfo:BleDeviceInfo):
        super().__init__()

        self.__bleDeviceInfo = bleDeviceInfo
        
        self.__queue = Queue(maxsize=3)
        
        self.__data = ""
        self.__data_timestamp = 0

        self.__thread:Thread = Thread(target = self.__ThreadSend)
        self.__thread.start()
        self.__thread:Thread = Thread(target = self.__ThreadRecv)
        self.__thread.start()

    async def __ClientProcess(self):
        try:
            async with BleakClient(
                self.__bleDeviceInfo.address,
                disconnected_callback = self.__on_Disconnected
            ) as client:
                self.Connected.emit(self.__bleDeviceInfo)
                await client.start_notify(TX_CHARACTERISTIC, self.__NotificationHandler)
            
                while True:
                    # Use await asyncio.wait_for(queue.get(), timeout=1.0) if you want a timeout for getting data.
                    type, data = self.__queue.get()
                
                    if (type >= 0):
                        bytes_date = data.encode()
                        await client.write_gatt_char(RX_CHARACTERISTIC, bytes_date)
                    else:
                        break;
        except:
            self.ConnectFailed.emit()
    
    def __ThreadSend(self):
        asyncio.run(self.__ClientProcess())
        
    def __ThreadRecv(self):
        while True:
            if (self.__data != ""):
                curr_timestamp = time.time_ns()
                if (curr_timestamp - self.__data_timestamp > 20 * 1000000):
                    self.DataReceived.emit(self.__data)
                    self.__data = ""
                
            time.sleep(0.01)
    
    def __on_Disconnected(self, client):
        self.Disconnected.emit()
        
    def __NotificationHandler(self, characteristic: BleakGATTCharacteristic, data: bytearray):
        self.__data += data.decode()
    
    def Close(self):
        self.__queue.put((-1, None))
        
    def SendMsg(self, msg):
        self.__queue.put((0, msg))

    @property
    def DeviceInfo(self):
        return self.__bleDeviceInfo